package logic;

import java.util.Arrays;
import java.util.HashMap;
import weka.core.Instance;
import weka.core.Instances;

class kNNhelper implements Comparable<kNNhelper> {
    public int idx;
    public int dist;
    public char label;

    @Override
    public int compareTo(kNNhelper t) {
            int compareDist = ((kNNhelper) t).dist;
            // ascending order
            return this.dist - compareDist;
        }    
}

    public class MushroomkNN extends MushroomClassifier {
        private int k;
        
        public MushroomkNN() {
            k = 1;
            algo = "kNN";
        }
        
        public MushroomkNN(int _k) {
            k = _k;
            algo = "kNN";
        }
    
        public static int getDistance(Instance training, Instance test) {
            int val = 0;
            String[] temp = training.toString().split(",");
            char[] training_attr = new char[temp.length];

            for (int i=0;i<temp.length;i++) {
                training_attr[i] = temp[i].charAt(0);
            }

            temp = test.toString().split(",");
            char[] test_attr = new char[temp.length];

            for (int i=0;i<temp.length;i++) {
                test_attr[i] = temp[i].charAt(0);
            }

            for (int i=0;i<training_attr.length;i++) {
                if (training_attr[i] != test_attr[i]) {
                    val++;
                }
            }

            return val;
        }

        public int[] getAllDistance(Instances training, Instance test) {
            int[] val = new int[training.numInstances()];

            for (int i=0;i<training.numInstances();i++) {
                val[i] = getDistance(training.instance(i), test);
            }
            return val;        
        }

        @Override
        public char classify(Instance test) {
            Instance in = initTestInstance(test);

            String[] temp = dataSet.toString().split(",");
            int lastAttributeIdx = temp.length - 1;

            kNNhelper[] help = new kNNhelper[dataSet.numInstances()];

            int[] k_class = new int[MushroomInfo.CLASS_VALUES_SHORT.length];
            for (int i=0;i<k_class.length;i++) {
                k_class[i] = 0;
            }

            for (int i = 0; i < dataSet.numInstances();i++) {
                help[i] = new kNNhelper();
                help[i].idx = i;
                help[i].dist = getDistance(dataSet.instance(i), in);
                String s = dataSet.instance(i).toString();
                help[i].label = s.substring(s.length()-1, s.length()).charAt(0);
            }

            Arrays.sort(help);

            // count label for k - lowest distance instance
            for (int i=0;i<k;i++) {
                for (int j=0;j<MushroomInfo.CLASS_VALUES_SHORT.length;j++) {
                    if (help[i].label==MushroomInfo.CLASS_VALUES_SHORT[j]) {
                        k_class[j]++;
                    }
                }
            }

            int max = k_class[0];
            int idx = 0;
            for (int i=0;i<k_class.length;i++) {
                if (k_class[i] > max) {
                    idx = i;
                }
            }

            return MushroomInfo.CLASS_VALUES_SHORT[idx];        
        }
        
        @Override
        public void learn(Instance i) {
            dataSet.add(new Instance(i));
        }

        @Override
        public void buildClassifier(Instances i) throws Exception {
            dataSet = new Instances(i);
        }

        public static void main(String[] args) {
            MushroomkNN kNN = new MushroomkNN(3);
            Instances inst = Loader.load("D:\\mushroom.arff");
            char value = '\0';

            try {
                kNN.buildClassifier(inst);
            } catch (Exception e) {
                e.printStackTrace();
            }
            
            for (int i = 0; i < inst.numInstances(); i++) {
                System.out.println((i+1) + ": " + kNN.classify(inst.instance(i)));
            }
       }
}
